#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include "link.h"

#define err(h) {printf("%s\n",h); return 1;}

void parse_header(FILE *, struct tag_link *);
void parse_shell_id(FILE *, struct tag_link *);
void parse_file_location(FILE *, struct tag_link *);
void parse_local_volume(FILE *, struct tag_link *); 
void parse_network_volume(FILE *, struct tag_link *); 
void parse_flags(int, struct tag_link *);
void parse_attributes(int, struct tag_link *);
void parse_type(int, struct tag_link *);
wchar_t *read_string(FILE *);

int shell_id, file_dir, directory,desc, rel_path, work_dir, cmd_line, icon, local, network;

int main(int argc, char *argv[]) {
	FILE *fp;
	struct tag_link *link = malloc(sizeof(*link));

	if(argc<2) {
		if((fp=fopen("g.lnk", "r"))==NULL)
			err("Could not open prog.lnk, give someother link");
	}
	else {
		if((fp=fopen(argv[1], "r"))==NULL)
			err("Could not open input file");
	}

	parse_header(fp, link);

	if(shell_id)
		parse_shell_id(fp, link);

	parse_file_location(fp, link);

	if(desc) { //descriptive string
		link->desc_str = read_string(fp);
		_tprintf("Descriptive string is %S\n",link->desc_str);

	}
	if(rel_path) { //relative path string
		link->path = read_string(fp);
		_tprintf("Relative path is %S\n", link->path);
	}
	if(work_dir) {
		link->dir = read_string(fp);
		_tprintf("Working directory is %S\n", link->dir);
	}
	if(cmd_line) {
		link->cmd = read_string(fp);
		_tprintf("Command line arguments are %S\n", link->cmd);
	}
	if(icon) {
		link->icon_str = read_string(fp);
		_tprintf("Icon is %S\n", link->icon_str);
	}
	fclose(fp);
	return 0;
}

void parse_flags(int flags, struct tag_link *link) {
	shell_id = file_dir = desc = rel_path = work_dir = cmd_line = icon = 0;
	if(flags&1)
		shell_id = 1;
	if(flags&2)
		file_dir = 1;
	if(flags&4)
		desc = 1;
	if(flags&8)
		rel_path = 1;
	if(flags&16)
		work_dir = 1;
	if(flags&32)
		cmd_line = 1;
	if(flags&64)
		icon = 1;

	link->shell_id = shell_id;
	link->file_dir = file_dir;
	link->desc = desc;
	link->rel_path = rel_path;
	link->work_dir = work_dir;
	link->cmd_line = cmd_line;
	link->icon = icon;

	printf("\nFlags are: \n");
	if(shell_id)
		printf("Has shell id structure\n");
	if(file_dir)
		printf("Points to a file or directory\n");
	if(desc)
		printf("Has descriptive string\n");
	if(rel_path)
		printf("Has relative path\n");
	if(work_dir)
		printf("Has working directory\n");
	if(cmd_line)
		printf("Has command line arguments\n");
	if(icon)
		printf("Has an icon\n");
}

void parse_attributes(int attributes, struct tag_link *link) {
	printf("\nAttributes are: \n");
	if(attributes&1) {
		link->read_only = 1;
		printf("Read only\n");
	}
	if(attributes&2) {
		link->hidden = 1;
		printf("Hidden\n");
	}
	if(attributes&4) {
		link->sys_file = 1;
		printf("System File\n");
	}
	if(attributes&8) {
		link->volume_label = 1;
		printf("Volume label\n");
	}
	if(attributes&16) {
		link->directory = 1;
		directory = 1;
		printf("Directory\n");
	}
	if(attributes&32) {
		link->archive = 1;
		printf("Archive\n");
	}
	if(attributes&64) {
		link->encrypted = 1;
		printf("Encrypted\n");
	}
	if(attributes&128) {
		link->normal = 1;
		printf("Might be normal\n");
	}
	if(attributes&256) {
		link->temporary = 1;
		printf("Temporary\n");
	}
	//there are others left too
}

void parse_type(int type, struct tag_link *link) {
	switch(type) {
	case 0: printf("Unknown\n"); break;
	case 1: printf("No root directory\n"); break;
	case 2: printf("Removable media\n"); break;
	case 3: printf("Fixed media like hard disk\n"); break;
	case 4: printf("Remote location\n"); break;
	case 5: printf("CD-ROM\n"); break;
	case 6: printf("Ram drive\n"); break;
	default: printf("Unknown type\n");
	}
}

void parse_header(FILE *fp, struct tag_link *link) {
	struct tag_link_header *header, h;

	header = malloc(sizeof(*header));
	fread(header, sizeof(*header), 1, fp); //must be sizeof(*header) not sizeof(header)
	//ate a whole half a hour for finding this bug
	h = *header;
	printf("magic: %X\nflags: %X\nattributes: %X\nFile length: %d\n",h.magic, h.flags, h.attributes, h.file_length);
	link->icon_number = h.icon_number;
	printf("Icon number: %d\n",h.icon_number);
	parse_flags(h.flags, link);
	parse_attributes(h.attributes, link);
	printf("%d\t%d\n",h.zero[0], h.zero[1]);
	free(header);
}

//reads the unicode string pointed by the file pointer
//assumes the first unsigned integer is the length of the string
//used for icon, command line, paths etc.
wchar_t * read_string(FILE *fp) { 
	int i, j;
	unsigned char k, l;
	char str_len[2];
	wchar_t *str;

	fread(str_len, 2, 1, fp);
	//printf("str_len[0] = %x\tstr_len[1] = %x\n",str_len[0], str_len[1]);
	i = str_len[1]+8*str_len[0];
	printf("Length of icon string is %x\n",i);
	str = malloc(sizeof(*str)*(i+1));
	fread(str, sizeof(*str), i, fp);
	str[i] = _T('\0');
	for(j=0; j<i; j++) {
		k = str[j]&0x00ff;
		l = str[j]>>8;
		str[j] = k*8 + l;
	}
	return str;
}

//doesn't actually parse it, just bypasses it
void parse_shell_id(FILE *fp, struct tag_link *link) {
	unsigned short int len; //for shell_id length
	printf("By-passing shell id\n");
	fread(&len, sizeof(len), 1, fp);
	printf("Size of shell id is %X", len);
	fseek(fp, len, 1); //1 - for the current position
}

void parse_file_location(FILE *fp, struct tag_link *link) {
	int i;
	struct tag_file_location_info *file_location, fl;

	file_location = malloc(sizeof(*file_location));
	fread(file_location, sizeof(*file_location), 1, fp);

	fl = *file_location;

	printf("\nFile Location Info\n");
	printf("Length of the structure: %d\n",fl.length);
	printf("Offset %X\n",fl.offset);

	local = network = 0;
	if(fl.flags&1) {
		link->local = 1;
		local = 1;
		printf("Available on local volume\n");
	}
	if(fl.flags&2) {
		link->network = 1;
		network = 1;
		printf("Available on a network\n");
	}

	if(local)
		parse_local_volume(fp, link); 

	if(network) //network path
		parse_network_volume(fp, link);
	free(file_location);
}

void parse_local_volume(FILE *fp, struct tag_link *link) {
	struct tag_local_volume_table *local_volume, lv;
	char base_path[255], volume_label[100];
	int i;

	local_volume = malloc(sizeof(*local_volume));
	fread(local_volume, sizeof(*local_volume), 1, fp);
	lv = *local_volume;
	printf("\nLocal volume: \n");
	printf("Length of local volume %d\n", lv.length);
	printf("Type of volume %d\n", lv.type);
	parse_type(lv.type, link);
	printf("Volume serial number %X\n", lv.serial_number);
	printf("Offset of volume name %d\n", lv.volume_name);
	//doing something equivilent to fscanf coz fscanf stops at spaces. this or due to someother
	//reason it doesn't work here
	i = 0;
	while((volume_label[i] = fgetc(fp))!='\0') {
		i++;
		if(i>100) {
			printf("Too big volume label\n");
			return;
		}
	}
	printf("Volume lable is %s\n",volume_label);
	i = 0;
	while((base_path[i] = fgetc(fp))!='\0') {
		i++;
		if(i>255) {
			printf("Too big base path");
			return;
		}
	}
	base_path[i] = '\0';
	strcpy(link->base_path, base_path);
	printf("Base path is %s\n",base_path);
	free(local_volume);
}

void parse_network_volume(FILE *fp, struct tag_link *link) {
	struct tag_network_volume_table *network_volume, nv;
	int i;
	char final_path[255], share_name[255];

	network_volume = malloc(sizeof(*network_volume));
	fread(network_volume, sizeof(*network_volume), 1, fp);
	nv = *network_volume;

	printf("\nNetwork volume: \n");
	printf("Length %d\n", nv.length);
	i = 0;
	while((share_name[i] = fgetc(fp))!='\0') {
		i++;
		if(i>255) {
			printf("Too share name");
			return;
		}
	}
	share_name[i] = '\0';
	strcpy(link->share_name, share_name);
	printf("%s\n",share_name);

	//if the network structure is present, the final path string should be read
	while((final_path[i] = fgetc(fp))!='\0') {
		i++;
		if(i>255) {
			printf("Too big final path");
			return;
		}
	}
	final_path[i] = '\0';
	strcpy(link->final_path, final_path);
	printf("%s\n",final_path);
	free(network_volume);
}
